/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 */
#include "stdlib.h"
#include <rtthread.h>
#include <rtdevice.h>

#ifdef RT_USING_SERIAL

#include "board.h"
#include "drv_common.h"

#if defined(BSP_USING_UART1) && defined(NRF52832_XXAA)
#undef BSP_USING_UART1
#define BSP_USING_UART0
#endif

struct nrf_uart_config
{
    const char *name;
    nrfx_uarte_t uarte;
    const char *rx_pin_name;
    const char *tx_pin_name;
    struct rt_serial_device serial;
    uint8_t dat_buff;
    uint8_t dat_rx_flag;
};

enum
{
#ifdef BSP_USING_UART0
    UART0_INDEX,
#endif
#ifdef BSP_USING_UART1
    UART1_INDEX,
#endif
};

struct nrf_uart_config uart_obj[] =
{
#ifdef BSP_USING_UART0
        {
#ifdef NRF52832_XXAA
                .name = "uart1",
#else
                .name = "uart0",
#endif
                .uarte = NRFX_UARTE_INSTANCE(0),
#ifdef NRF52832_XXAA
                .rx_pin_name = BSP_UART1_RX_PIN,
                .tx_pin_name = BSP_UART1_TX_PIN,
#else
                .rx_pin_name = BSP_UART0_RX_PIN,
                .tx_pin_name = BSP_UART0_TX_PIN,
#endif
        },
#endif
#ifdef BSP_USING_UART1
        {
                .name = "uart1",
                .uarte = NRFX_UARTE_INSTANCE(1),
                .rx_pin_name = BSP_UART1_RX_PIN,
                .tx_pin_name = BSP_UART1_TX_PIN,
        },
#endif
};

//pin_name eg P0.6
static uint32_t get_pin_by_name(const char* pin_name)
{
    RT_ASSERT(pin_name[0] == 'P');
    uint16_t port = pin_name[1] - 'A';
    uint16_t pin = atoi((char*) &pin_name[2]);

    return GET_PIN(port,pin);
}

#ifdef BSP_USING_UART0
void UARTE0_UART0_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    if (nrf_uarte_int_enable_check(uart_obj[UART0_INDEX].uarte.p_reg, NRF_UARTE_INT_ERROR_MASK)
            && nrf_uarte_event_check(uart_obj[UART0_INDEX].uarte.p_reg, NRF_UARTE_EVENT_ERROR))
    {
        nrf_uarte_event_clear(uart_obj[UART0_INDEX].uarte.p_reg, NRF_UARTE_EVENT_ERROR);
    }

    if (nrf_uarte_int_enable_check(uart_obj[UART0_INDEX].uarte.p_reg, NRF_UARTE_INT_ENDRX_MASK)
            && nrf_uarte_event_check(uart_obj[UART0_INDEX].uarte.p_reg, NRF_UARTE_EVENT_ENDRX))
    {
        uart_obj[UART0_INDEX].dat_rx_flag = 1;
        nrf_uarte_event_clear(uart_obj[UART0_INDEX].uarte.p_reg, NRF_UARTE_EVENT_ENDRX);
        nrf_uarte_rx_buffer_set(uart_obj[UART0_INDEX].uarte.p_reg, &uart_obj[UART0_INDEX].dat_buff, 1);
        nrf_uarte_task_trigger(uart_obj[UART0_INDEX].uarte.p_reg, NRF_UARTE_TASK_STARTRX);
        rt_hw_serial_isr(&uart_obj[UART0_INDEX].serial, RT_SERIAL_EVENT_RX_IND);
    }

    if (nrf_uarte_int_enable_check(uart_obj[UART0_INDEX].uarte.p_reg, NRF_UARTE_INT_TXDRDY_MASK)
            && nrf_uarte_event_check(uart_obj[UART0_INDEX].uarte.p_reg, NRF_UARTE_EVENT_TXDRDY))
    {
        nrf_uarte_event_clear(uart_obj[UART0_INDEX].uarte.p_reg, NRF_UARTE_EVENT_TXDRDY);
        rt_hw_serial_isr(&uart_obj[UART0_INDEX].serial, RT_SERIAL_EVENT_TX_DONE);
    }

    if (nrf_uarte_event_check(uart_obj[UART0_INDEX].uarte.p_reg, NRF_UARTE_EVENT_RXTO))
    {
        nrf_uarte_event_clear(uart_obj[UART0_INDEX].uarte.p_reg, NRF_UARTE_EVENT_RXTO);
        rt_hw_serial_isr(&uart_obj[UART0_INDEX].serial, RT_SERIAL_EVENT_RX_TIMEOUT);
    }

    /* leave interrupt */
    rt_interrupt_leave();
}
#endif

#ifdef BSP_USING_UART1
void UARTE1_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    if (nrf_uarte_int_enable_check(uart_obj[UART1_INDEX].uarte.p_reg, NRF_UARTE_INT_ERROR_MASK)
            && nrf_uarte_event_check(uart_obj[UART1_INDEX].uarte.p_reg, NRF_UARTE_EVENT_ERROR))
    {
        nrf_uarte_event_clear(uart_obj[UART1_INDEX].uarte.p_reg, NRF_UARTE_EVENT_ERROR);
    }

    if (nrf_uarte_int_enable_check(uart_obj[UART1_INDEX].uarte.p_reg, NRF_UARTE_INT_ENDRX_MASK)
            && nrf_uarte_event_check(uart_obj[UART1_INDEX].uarte.p_reg, NRF_UARTE_EVENT_ENDRX))
    {
        uart_obj[UART1_INDEX].dat_rx_flag = 1;
        nrf_uarte_event_clear(uart_obj[UART1_INDEX].uarte.p_reg, NRF_UARTE_EVENT_ENDRX);
        nrf_uarte_rx_buffer_set(uart_obj[UART1_INDEX].uarte.p_reg, &uart_obj[UART1_INDEX].dat_buff, 1);
        nrf_uarte_task_trigger(uart_obj[UART1_INDEX].uarte.p_reg, NRF_UARTE_TASK_STARTRX);
        rt_hw_serial_isr(&uart_obj[UART1_INDEX].serial, RT_SERIAL_EVENT_RX_IND);
    }

    if (nrf_uarte_int_enable_check(uart_obj[UART1_INDEX].uarte.p_reg, NRF_UARTE_INT_TXDRDY_MASK)
            && nrf_uarte_event_check(uart_obj[UART1_INDEX].uarte.p_reg, NRF_UARTE_EVENT_TXDRDY))
    {
        nrf_uarte_event_clear(uart_obj[UART1_INDEX].uarte.p_reg, NRF_UARTE_EVENT_TXDRDY);
        rt_hw_serial_isr(&uart_obj[UART1_INDEX].serial, RT_SERIAL_EVENT_TX_DONE);
    }

    if (nrf_uarte_event_check(uart_obj[UART1_INDEX].uarte.p_reg, NRF_UARTE_EVENT_RXTO))
    {
        nrf_uarte_event_clear(uart_obj[UART1_INDEX].uarte.p_reg, NRF_UARTE_EVENT_RXTO);
        rt_hw_serial_isr(&uart_obj[UART1_INDEX].serial, RT_SERIAL_EVENT_RX_TIMEOUT);
    }

    /* leave interrupt */
    rt_interrupt_leave();
}
#endif

static rt_err_t _uart_cfg(struct rt_serial_device *serial, struct serial_configure *cfg)
{
    nrfx_uarte_config_t config = NRFX_UARTE_DEFAULT_CONFIG;
    struct nrf_uart_config *uart;

    RT_ASSERT(serial != RT_NULL);
    RT_ASSERT(cfg != RT_NULL);

    uart = rt_container_of(serial, struct nrf_uart_config, serial);

    switch (cfg->baud_rate)
    {
    case 115200:
        config.baudrate = NRF_UARTE_BAUDRATE_115200;
        break;

    case 9600:
        config.baudrate = NRF_UARTE_BAUDRATE_9600;
        break;

    default:
        config.baudrate = NRF_UARTE_BAUDRATE_115200;
        break;
    }

    if (cfg->parity == PARITY_NONE)
    {
        config.parity = NRF_UARTE_PARITY_EXCLUDED;
    }
    else
    {
        config.parity = NRF_UARTE_PARITY_INCLUDED;
    }
    config.pselrxd = get_pin_by_name(uart->rx_pin_name);
    config.pseltxd = get_pin_by_name(uart->tx_pin_name);

    nrfx_uarte_init(&uart->uarte, (nrfx_uarte_config_t const *)&config, RT_NULL);

    nrf_uarte_event_clear(uart->uarte.p_reg, NRF_UARTE_EVENT_ENDTX);
    nrf_uarte_event_clear(uart->uarte.p_reg, NRF_UARTE_EVENT_ENDRX);
    nrf_uarte_event_clear(uart->uarte.p_reg, NRF_UARTE_EVENT_ERROR);
    nrf_uarte_event_clear(uart->uarte.p_reg, NRF_UARTE_EVENT_RXTO);
    nrf_uarte_int_enable(uart->uarte.p_reg, NRF_UARTE_INT_ENDRX_MASK |
                                            NRF_UARTE_INT_ERROR_MASK |
                                            NRF_UARTE_INT_RXTO_MASK);
    NVIC_SetPriority(nrfx_get_irq_number((void *)uart->uarte.p_reg), config.interrupt_priority);
    NVIC_EnableIRQ(nrfx_get_irq_number((void *)uart->uarte.p_reg));

    nrf_uarte_rx_buffer_set(uart->uarte.p_reg, &uart->dat_buff, 1);
    nrf_uarte_task_trigger(uart->uarte.p_reg, NRF_UARTE_TASK_STARTRX);
    return RT_EOK;
}

static rt_err_t _uart_ctrl(struct rt_serial_device *serial, int cmd, void *arg)
{
    struct nrf_uart_config *uart;
    RT_ASSERT(serial != RT_NULL);
    uart = rt_container_of(serial, struct nrf_uart_config, serial);

    switch (cmd)
    {
    /* disable interrupt */
    case RT_DEVICE_CTRL_CLR_INT:
        nrf_uarte_task_trigger(uart->uarte.p_reg, NRF_UARTE_TASK_STOPRX);
        nrf_uarte_int_disable(uart->uarte.p_reg, NRF_UARTE_INT_RXDRDY_MASK
                             | NRF_UARTE_INT_RXTO_MASK
                             | NRF_UARTE_INT_ERROR_MASK);
        NVIC_DisableIRQ(nrfx_get_irq_number((void *)uart->uarte.p_reg));
        break;

    /* enable interrupt */
    case RT_DEVICE_CTRL_SET_INT:
        nrf_uarte_event_clear(uart->uarte.p_reg, NRF_UARTE_EVENT_ENDRX);
        nrf_uarte_event_clear(uart->uarte.p_reg, NRF_UARTE_EVENT_RXTO);
        nrf_uarte_event_clear(uart->uarte.p_reg, NRF_UARTE_EVENT_ERROR);
        /* Enable RX interrupt. */
        nrf_uarte_int_enable(uart->uarte.p_reg, NRF_UARTE_INT_ENDRX_MASK
                            | NRF_UARTE_INT_RXTO_MASK
                            | NRF_UARTE_INT_ERROR_MASK);
        NVIC_SetPriority(nrfx_get_irq_number((void *)uart->uarte.p_reg), 6);
        NVIC_EnableIRQ(nrfx_get_irq_number((void *)uart->uarte.p_reg));
        nrf_uarte_task_trigger(uart->uarte.p_reg, NRF_UARTE_TASK_STARTRX);
        break;

    default:
        return RT_ERROR;
    }

    return RT_EOK;
}

static int _uart_putc(struct rt_serial_device *serial, char c)
{
    struct nrf_uart_config *uart;
    RT_ASSERT(serial != RT_NULL);
    uart = rt_container_of(serial, struct nrf_uart_config, serial);

    if(nrfx_uarte_tx(&uart->uarte, (uint8_t const *)&c, 1) == NRFX_SUCCESS)
        return 1;
    else
        return 0;
}

static int _uart_getc(struct rt_serial_device *serial)
{
    struct nrf_uart_config *uart;
    RT_ASSERT(serial != RT_NULL);
    uart = rt_container_of(serial, struct nrf_uart_config, serial);

    if(uart->dat_rx_flag == 1)
    {
        uart->dat_rx_flag = 0;
        return uart->dat_buff;
    }
    else
        return -1;
}

static rt_size_t nrf_dma_transmit(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size, int direction)
{
    struct nrf_uart_config *uart;
    RT_ASSERT(serial != RT_NULL);
    uart = rt_container_of(serial, struct nrf_uart_config, serial);

    if (size == 0)
    {
        return 0;
    }

    if (RT_SERIAL_DMA_TX == direction)
    {
        if (nrfx_uarte_tx(&uart->uarte, buf, size) == NRFX_SUCCESS)
        {
            return size;
        }
        else
        {
            return 0;
        }
    }
    return 0;
}

static struct rt_uart_ops _uart_ops =
{
    _uart_cfg,
    _uart_ctrl,
    _uart_putc,
    _uart_getc,
    nrf_dma_transmit,
};

int rt_hw_uart_init(void)
{
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
    rt_err_t result = 0;

    for (int i = 0; i < sizeof(uart_obj) / sizeof(uart_obj[0]); i++)
    {
        uart_obj[i].serial.ops    = &_uart_ops;
        uart_obj[i].serial.config = config;
        /* register UART device */
        result = rt_hw_serial_register(&uart_obj[i].serial, uart_obj[i].name,
                                       RT_DEVICE_FLAG_RDWR
                                       | RT_DEVICE_FLAG_INT_RX
                                       | RT_DEVICE_FLAG_INT_TX
                                       | RT_DEVICE_FLAG_DMA_RX
                                       | RT_DEVICE_FLAG_DMA_TX
                                       , NULL);
        RT_ASSERT(result == RT_EOK);
    }
    return 0;
}
INIT_BOARD_EXPORT(rt_hw_uart_init);

#endif /* RT_USING_SERIAL */
